using System;
using System.Collections;
using System.Collections.Specialized;
using System.Runtime.CompilerServices;
using System.Data;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using W =Microsoft.Office.Interop.Word;
using E = Microsoft.Office.Interop.Excel;
using Dimok;
using Dimok.Exceptions;
using Dimok.Images;
using ConfigWord = Dimok.Config.imaging.Word;

namespace Dimok.Documents {

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Word. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class Word : MarshalByRefObject{

		#region FillForm (Hashtable)
		/*

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill form. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="fileIn">	The file in. </param>
		/// <param name="fields">	The fields. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public byte[] FillForm(byte[] fileIn,Hashtable fields){
				string fileName =  FileManager.GetFileFromBytes(fileIn);
				return FileManager.GetBytesFromFile(FillForm(fileName,null,fields),true);
		}
		public string FillForm(string FileIn,string FileOut,Hashtable fields){
			if( FileOut == "" )FileOut = FileManager.GetSureFileName(FileIn+"","00");
			object fileIn = FileIn, fileOut = FileOut;
			object m = Missing.Value, not = false;
			W.ApplicationClass word = new W.ApplicationClass();
			W.DocumentClass doc = null;
			try{
				doc = (W.DocumentClass)word.Documents.Open(ref fileIn,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref not);
				FillForm(doc,fields);
				if( fileOut == null )doc.Save();
				else doc.SaveAs(ref fileOut,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m);
				return doc.FullName;
			}finally{
				if( doc != null )doc.Close(ref not,ref m,ref m);
				word.Quit(ref not,ref m,ref m);
			}
		}
		private void FillForm(W.DocumentClass doc,Hashtable fields){
			foreach( W.FormField field in doc.FormFields )
				if( fields.ContainsKey(field.Result) )
					field.Result = fields[field.Result]+"";
		}
		*/
		#endregion FillForm (Hashtable)

		#region FillForm (Connection)
		/*

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill form. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="fileIn">			The file in. </param>
		/// <param name="connection">	The connection. </param>
		/// <param name="sql">				The sql. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public byte[] FillForm(byte[] fileIn,string connection, string sql){
			string fileName =  FileManager.GetFileFromBytes(fileIn);
			try{
				return FileManager.GetBytesFromFile(FillForm(fileName,"",connection,sql),true);
			}finally{ File.Delete(fileName); }
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill form. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="fileNames">	List of names of the files. </param>
		/// <param name="connection">	The connection. </param>
		/// <param name="sql">				The sql. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string[] FillForm(string[] fileNames,string connection, string sql){
			ArrayList al = new ArrayList();
			foreach( string fileName in fileNames )
				al.Add(FillForm(fileName,"",connection,sql));
			return (string[])al.ToArray(typeof(string));
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill form. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="Exception">	Thrown when exception. </exception>
		///
		/// <param name="FileIn">			The file in. </param>
		/// <param name="FileOut">		The file out. </param>
		/// <param name="connection">	The connection. </param>
		/// <param name="sql">				The sql. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string FillForm(string FileIn,string FileOut,string connection, string sql){
			if( FileOut == null )throw new Exception("FileOut parameter must not be <null>.");
			if( FileOut == "" )FileOut = FileManager.GetSureFileName(FileIn+"","00");
			object fileIn = FileIn, fileOut = FileOut;
			object m = Missing.Value, not = false, yes = true;
			W.ApplicationClass word = new W.ApplicationClass();
			W.DocumentClass doc = null;
			try{
				doc = (W.DocumentClass)word.Documents.Open(ref fileIn,ref m,ref yes,ref not,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref not);
				W.Document docMerge = FillForm(doc,(object)connection,(object)sql);
				docMerge.SaveAs(ref fileOut,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m);
				docMerge.Close(ref m,ref m,ref m);
				return FileOut;
			}finally{
				if( doc != null ){
					doc.Activate();
					doc.Close(ref not,ref m,ref m);
				}
				word.Quit(ref not,ref m,ref m);
			}
		}
		private W.Document FillForm(W.DocumentClass docTempl,object connection,object sql){
			object m = Missing.Value;
			W.Application app = docTempl.Application;
			float leftMargin = docTempl.Content.PageSetup.LeftMargin;
			float rightMargin = docTempl.Content.PageSetup.RightMargin;
			docTempl.MailMerge.OpenDataSource("",ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref connection, ref sql, ref m);
			docTempl.MailMerge.Execute(ref m);
			W.Document docActive = docTempl.Application.ActiveDocument;
			docActive.Content.PageSetup.LeftMargin = app.InchesToPoints(1);
			docActive.Content.PageSetup.RightMargin = app.InchesToPoints(1);
			if( ConfigWord.RemoveFieldFormats )
				docTempl.Application.ActiveDocument.Content.HighlightColorIndex = W.WdColorIndex.wdNoHighlight;
			return docTempl.Application.ActiveDocument;
		}
		*/
		#endregion FillForm (Connection)

		#region FillForm (DataTable)

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill form. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="files">	. </param>
		/// <param name="table">	. </param>
		///
		/// <returns>	Word document. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public byte[] FillForm(byte[][] files,DataTable table){return FillForm(files,table,false);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill Word document form. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="Exception">	Thrown when exception. </exception>
		///
		/// <param name="files">	. </param>
		/// <param name="table">	. </param>
		/// <param name="pdfOut">	If [true] - return a PDF document. ([false] is default) </param>
		///
		/// <returns>	Word or PDF document. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public byte[] FillForm(byte[][] files,DataTable table,bool pdfOut){
			ArrayList alFiles = new ArrayList();
			foreach( byte[] file in files )
				switch( Enums.GetPageType(file) ){
					case PageTypes.WORD:
						alFiles.Add(FillForm(file,table,pdfOut));	break;
					case PageTypes.PDF:
						if( pdfOut )alFiles.Add(file);
						else throw new Exception("Can't make *.DOC out of *.PDF.");
						break;
					default:
						throw new Exception("Wrong file type");
				}
			return pdfOut ? PDF.CombinePDF(alFiles) : CombineDocs(alFiles);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill form. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="file">		. </param>
		/// <param name="table">	. </param>
		///
		/// <returns>	Word document. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public byte[] FillForm(byte[] file,DataTable table){return FillForm(file,table,false);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill Word document form. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="file">		Word file name. </param>
		/// <param name="table">	Data table. </param>
		/// <param name="pdfOut">	If [true] - return a PDF document. ([false] is default) </param>
		///
		/// <returns>	Word or PDF document. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public byte[] FillForm(byte[] file,DataTable table,bool pdfOut){
			string fileName = FileManager.GetFileFromBytes(file,".doc");
			try{
				string fileOut = FillForm(fileName,table);
				if( pdfOut )fileOut = PDF.AnyToPDF(fileOut,"",true);
				return FileManager.GetBytesFromFile(fileOut,true);
			}finally{
				try{
					File.Delete(fileName);
				}catch(Exception exc){
					ClientException.Publish(exc);
				}
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill form. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="FileIn">	The file in. </param>
		/// <param name="table">	. </param>
		///
		/// <returns>	Word document. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string FillForm(string FileIn,DataTable table){
			return FillForm(FileIn,"",table);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill form. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="FileIn">		The file in. </param>
		/// <param name="FileOut">	The file out. </param>
		/// <param name="table">		. </param>
		///
		/// <returns>	Word document. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string FillForm(string FileIn,string FileOut,DataTable table){
			//if( FileOut == null )throw new Exception("FileOut parameter must not be <null>.");
			if( FileOut == "" )FileOut = FileManager.GetSureFileName(FileIn+"","00");
			if( FileOut != null )FileOut = FileManager.BuildFullFileName(FileOut,true);
			object fileIn = FileIn, fileOut = FileOut;
			return FillForm(fileIn,fileOut,table);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill form. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="Exception">	Thrown when exception. </exception>
		///
		/// <param name="templFileName">	Filename of the templ file. </param>
		/// <param name="fileOut">				Full/partial name of output file. </param>
		/// <param name="table">					. </param>
		///
		/// <returns>	Word document. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		[MethodImplAttribute(MethodImplOptions.Synchronized)]
		public static string FillForm(object templFileName,object fileOut,DataTable table){
			object m = Missing.Value, not = false, yes = true, e = "";
			W.ApplicationClass app = new W.ApplicationClass();
			try{
				//throw new ApplicationException("Template:"+templFileName);
				W.DocumentClass docTempl = (W.DocumentClass)app.Documents.Open(ref templFileName,ref m,ref yes,ref not,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref not);
				W.MailMergeFields mmFields = docTempl.MailMerge.Fields;
				if( mmFields.Count == 0 ){
					if( templFileName.ToString() != fileOut.ToString() )
						File.Copy(templFileName+"",fileOut+"");
					return fileOut+"";
				}
				foreach(W.MailMergeField mmField in mmFields){
					string fieldName = Regex.Split(Regex.Replace(mmField.Code.Text,"\\s+"," ")," ")[2];
					if( mmField.Type == W.WdFieldType.wdFieldMergeField && !table.Columns.Contains(fieldName) )
						throw new Exception("Field "+fieldName+" does not exists in DataSource.");
				}
				float leftMargin = app.PointsToInches(docTempl.Content.PageSetup.LeftMargin);
				float rightMargin = app.PointsToInches(docTempl.Content.PageSetup.RightMargin);
				//string dataFileName = BuildDataSourceExcel(table,Path.ChangeExtension(docTempl.FullName,"xls"));
				//string dataFileName = CreateDataSource(docTempl,table,"C:\\MailMerge.doc");
				string dataFileName = "";
				try{
					dataFileName = BuildDataSourceText(table,"C:\\MailMerge.txt");
					docTempl.MailMerge.OpenDataSource(dataFileName,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m);
					if( fileOut != null )docTempl.MailMerge.Execute(ref m);
					else return SaveAs(docTempl,FileManager.BuildFullFileName("","doc",true),false);
				}finally{
					docTempl.Activate();
					docTempl.Close(ref not,ref m,ref m);
					if( dataFileName != "" )File.Delete(dataFileName);
				}
				W.Document docActive = app.ActiveDocument;
				docActive.Content.PageSetup.LeftMargin = app.InchesToPoints(leftMargin);
				docActive.Content.PageSetup.RightMargin = app.InchesToPoints(rightMargin);
				if( ConfigWord.RemoveFieldFormats )
					docActive.Content.HighlightColorIndex = W.WdColorIndex.wdNoHighlight;
				return SaveAs(docActive,fileOut,true);
			}finally{
				app.Quit(ref not,ref m,ref m);
			}
		}
		#endregion FillForm (DataTable)

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Creates a data source. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="ClientException">	Thrown when client. </exception>
		///
		/// <param name="doc">						The document. </param>
		/// <param name="dt">							The dt. </param>
		/// <param name="dataSourceFile">	The data source file. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static string CreateDataSource(W.Document doc,DataTable dt,string dataSourceFile){
			object fileDS = FileManager.BuildFullFileName(dataSourceFile,"doc",true);
			try{
				File.Delete(fileDS+"");
			}catch(Exception exc){ throw new ClientException(exc,"FileName",fileDS+""); }
			object m = Missing.Value, not = false, yes = true;
			StringList slHeader = BuildHeader(dt);
			object header = slHeader.Join(",");
			doc.MailMerge.CreateDataSource(ref fileDS,ref m,ref m,ref header,ref m,ref m,ref m,ref m,ref m);
			W.Document docDS = doc.Application.Documents.Open(ref fileDS,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m);
			for(int r = 0; r < slHeader.Count; r++){
				string text = docDS.Tables[1].Cell(1,r+1).Range.Text;
				if( text == "" )text = slHeader[r];
			}
			for(int c = 0; c < dt.Columns.Count; c++ )
				docDS.Tables[1].Cell(2,c+1).Range.InsertAfter(dt.Rows[0][c]+"");
			docDS.Close(ref yes,ref m,ref m);
			return fileDS+"";
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Builds a header. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="dt">	The dt. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static StringList BuildHeader(DataTable dt){
			StringList slHeader = new StringList();
			foreach( DataColumn col in dt.Columns)
				slHeader.Add(col.ColumnName);
			return slHeader;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Build data source for MailMerge in MS Word. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="table">		. </param>
		/// <param name="fileOut">	Full/partial name of output file. </param>
		///
		/// <returns>	name of data source file. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string BuildDataSourceText( DataTable table,string fileOut){
			return FileManager.GetFileFromBytes(BuildDataSourceText(table),FileManager.BuildFullFileName(fileOut,false),false);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Build data source for MailMerge in MS Word. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="table">	. </param>
		///
		/// <returns>	content of the data source file. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string BuildDataSourceText( DataTable table){
			ArrayList alHeader = new ArrayList();
			ArrayList alFields = new ArrayList();
			foreach( DataColumn col in table.Columns ){
				alHeader.Add(col.ColumnName);
				string v = table.Rows.Count > 0 ? table.Rows[0][col]+"" : "";
				alFields.Add("\""+v.Replace("\r","").Replace("\"","\"\"")+"\"");
			}
			string[] header = (string[])alHeader.ToArray(typeof(string));
			string[] values = (string[])alFields.ToArray(typeof(string));
			return string.Join("\t",header)+"\r\n"+string.Join("\t",values);
		}
		//ConnectString : "DSN=Excel Files;DBQ=C:\Data\Dimok\Docs\AMFC\FrontEnd\Docs\DataSource.xls;DriverId=790;MaxBufferSize=2048;PageTimeout=5;"
		//Name : "C:\Data\Dimok\Docs\AMFC\FrontEnd\Docs\DataSource.xls"
		//QueryString : "SELECT * FROM `Table1`"

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Builds a data source excel. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="table">		. </param>
		/// <param name="fileOut">	Full/partial name of output file. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private string BuildDataSourceExcel( DataTable table,string fileOut){
			fileOut = FileManager.BuildFullFileName(fileOut,true);
			object m = Missing.Value,no = false;
			E.ApplicationClass app = new E.ApplicationClass();
			E.Workbook book = app.Workbooks.Add(m);
			E.Worksheet sheet = (E.Worksheet)book.ActiveSheet;
			try{
				int c = 0;
				for( ; c < table.Columns.Count; c++ ){
					DataColumn col = table.Columns[c];
					((E.Range)sheet.Cells[1,c+1]).Value = col.ColumnName;
					((E.Range)sheet.Cells[2,c+1]).Value = table.Rows[0][col];
				}
				book.Names.Add("Table1",m,m,m,m,m,m,m,m,"=Sheet1!R1C1:R2C"+c,m);
				book.SaveAs(fileOut,m,m,m,m,m,E.XlSaveAsAccessMode.xlNoChange,m,m,m,m);
				return fileOut;
			}finally{
				book.Close(no,m,m);
				app.Quit();
			}
		}

		#region CombineDocs

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Combine documents. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="files">	. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public byte[] CombineDocs(ArrayList files){return CombineDocs((byte[][])files.ToArray(typeof(byte[])));}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Combine documents. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="files">	. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public byte[] CombineDocs(byte[][] files){
			ArrayList fileNames = new ArrayList();
			foreach( byte[] file in files )
				fileNames.Add(FileManager.GetFileFromBytes(file));
			try{
				return FileManager.GetBytesFromFile(CombineDocs((string[])fileNames.ToArray(typeof(string)),""),true);
			}finally{
				foreach( string file in fileNames )
					File.Delete(file);
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Combine documents. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="fileNames">	List of names of the files. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string CombineDocs(string[] fileNames){return CombineDocs(fileNames,"");}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Combine documents. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="files">		. </param>
		/// <param name="fileOut">	Full/partial name of output file. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string CombineDocs(string[] files,string fileOut){
			if( fileOut == null || fileOut == "" )fileOut = FileManager.BuildFullFileName("Combined.doc",true);
			object m = Missing.Value, not = false;
			W.ApplicationClass word = new W.ApplicationClass();
			W.DocumentClass doc = (W.DocumentClass)word.Documents.Add(ref m,ref m,ref m,ref not);
			doc.Content.Select();
			try{
				foreach( string file in files )
					word.Selection.InsertFile(file,ref m,ref m,ref m,ref m);
				object o = fileOut;
				doc.SaveAs(ref o,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m);
				return fileOut;
			}finally{
				doc.Close(ref not,ref m,ref m);
				word.Quit(ref not,ref m,ref m);
			}
		}
		#endregion

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Saves as. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="doc">			The document. </param>
		/// <param name="fileName">	Filename of the file. </param>
		/// <param name="close">		true to close. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static string SaveAs(W.Document doc,object fileName ,bool close){
			object m = Missing.Value;
			doc.SaveAs(ref fileName,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m,ref m);
			if( close )doc.Close(ref m,ref m,ref m);
			return fileName+"";
		}
	}
}
